/* generate.c - code generation functions.
 *
 */
#include "generate.h"
#include "symtab.h"
#include "globals.h"

/* MINRANGE - minimum sequential run of tokens we will use as a range. */
#define MINRANGE    (4)

static
void    IntAdd(INTVECTOR* This, int Add)
    {
    int     NewSize;

    NewSize     = This->Size + 1;
    This->v     = (int*)realloc(This->v, NewSize*sizeof(int));
    This->v[This->Size] = Add;
    This->Size  = NewSize;
    }
static
int     IntGet(INTVECTOR* This, int Offset)
    {
    assert(Offset >= 0);
    assert(Offset < This->Size);
    return This->v[Offset];
    }

static
void    GrowOpcodes(TParseTables* Tables, size_t Size)
    {
    if(Tables->OpcodeSize < Size)
        {
        if(Tables->Opcodes == NULL)
            Tables->Opcodes = calloc(Size, sizeof(BLC_OPCODE));
        else if(Size > Tables->OpcodeSize)
            Tables->Opcodes = realloc(Tables->Opcodes, sizeof(BLC_OPCODE)*Size);
    
        assert(Tables->Opcodes != NULL);
        Tables->OpcodeSize  = Size;
        }
    assert(Size <= Tables->OpcodeSize);
    }

static
size_t  Store16(TParseTables* Tables, size_t IP, int Value)
    {
    assert(Tables != NULL);
    GrowOpcodes(Tables, IP+2);
    Tables->Opcodes[IP++]   = (unsigned char)(Value & 0x0FF);
    Tables->Opcodes[IP++]   = (unsigned char)((Value>>8) & 0x0FF);
    return IP;
    }

static
size_t  Store8(TParseTables* Tables, size_t IP, int Value)
    {
    assert(Tables != NULL);
    assert(Value < 0x0FF);
    GrowOpcodes(Tables, IP+1);
    Tables->Opcodes[IP++]   = (unsigned char)(Value & 0x0FF);
    return IP;
    }

static
size_t  StoreStr(TParseTables* Tables, size_t IP, const char* Str)
    {
    size_t  Len = strlen(Str)+1;

    GrowOpcodes(Tables, IP+Len);
    while(Len > 0)
        {
        Tables->Opcodes[IP++]   = *Str++;
        --Len;
        }

    return IP;
    }


/* TSelect - structure for grouping tokens by destination rule
 */
typedef struct  TSelRange
    {
    struct TSelRange*   Next;
    int         Start;
    int         End;
    }           TSelRange;
typedef struct  TSelect
    {
    int         Rule;       /* rule# (as returned by RuleAdd())     */
    int         MaxRange;   /* biggest sequential run of tokens     */
    int         NRanges;
    TSelRange*  Ranges;
    int         Count;      /* current count of tokens              */
    INTVECTOR   Tokens;     /* the actual token values              */
    }           TSelect;

/* SelectNew() - allocate array of TSelect structures.
 */
static
TSelect*    SelectNew(int Size)
    {
    TSelect*    Result;

    Result      = calloc(Size, sizeof(TSelect));
    assert(Result != NULL);
    return Result;
    }

static
void        SelectDestroy(TSelect* Table, int Size)
    {
    int     iSelect;

    for(iSelect=0; iSelect < Size; ++iSelect)
        {
        TSelRange*  RangeRover;
        TSelRange*  Temp;
        TSelect*    Select;

        Select  = Table + iSelect;
        for(RangeRover=Select->Ranges; RangeRover; )
            {
            Temp    = RangeRover->Next;
            free(RangeRover);
            RangeRover  = Temp;
            }
        free(Table[iSelect].Tokens.v);
        }
    free(Table);
    }


/* SelectAdd() - add a new rule/token pair to the table.
 *
 * Given a table (array) of TSelect structures, we either
 * discover that the rule already exists in the table and
 * add this token to its Tokens (symbol list), or else we
 * add this rule to the table and then do the same.
 *
 * The caller is responsible for making sure the table is
 * big enough that a new rule can be added to the end.
 */
static
int    SelectAdd(TSelect* Table, int Size, int Rule, TSymbol* Token)
    {
    int     iSelect;
    int     NewSize = Size;
    
    for(iSelect=0; iSelect < Size; ++iSelect)
        if(Table[iSelect].Rule == Rule)
            break;
    if(iSelect >= Size)
        {
        Table[iSelect].Rule     = Rule;
        ++NewSize;
        }
    IntAdd(&Table[iSelect].Tokens, Token->Value);

    return NewSize;
    }

/* SelectAddRange() - add range info to a selection set.
 *
 * This structure basically indicates there is a range of
 * sequential token values in the selection set. Ranges
 * are maintained in a linked list in order of range size,
 * with the biggest range at the front.
 */
static
void    SelectAddRange(TSelect* Select, int Start, int End)
    {
    TSelRange** Rover;
    TSelRange*  New;
    int         Size    = (End - Start) + 1;

printf("SelectAddRange(Start=%d, End=%d)\n", Start, End);
//    assert(Start >= 0);
//    assert(End < Select->Tokens.Size);
    New         = NEW(TSelRange);
    assert(New != NULL);
    New->Start  = Start;
    New->End    = End;
    Rover       = &Select->Ranges;
    /* scan existing ranges */
    while(*Rover)
        /* if this one is smaller range than us */
        if(((*Rover)->End - (*Rover)->Start) - 1 <= Size)
            break;
        else
            Rover   = &((*Rover)->Next);
    New->Next   = *Rover;
    *Rover      = New;
    ++Select->NRanges;
    }

/* SelectCmpRange() - compare the maximum range of two TSelect's
 *
 * Sort in descending order, as we want biggest ranges first.
 */
static
int     SelectCmpRange(const void* A, const void* B)
    {
    TSelect*    a;
    TSelect*    b;

    a   = (TSelect*)A;
    b   = (TSelect*)B;
    return -(a->NRanges - b->NRanges);
    }

/* SelectCmpCount() - compare the token count of two TSelect's
 *
 * Sort in descending order, as we want biggest stuff first.
 */
static
int     SelectCmpCount(const void* A, const void* B)
    {
    TSelect*    a;
    TSelect*    b;

    a   = (TSelect*)A;
    b   = (TSelect*)B;
    return -(a->Count - b->Count);
    }

/* IntCmp() - sort those integers in ascending order. */
static
int     IntCmp(const void* A, const void* B)
    {
    return ((int*)A)[0] - ((int*)B)[0];
    }

/* SelectSortByRange() - sort the selection table by range.
 *
 * We sort by who has the biggest range (run of sequential-valued
 * tokens). First, we calculate the biggest range in each TSelect,
 * storing it in the MaxRange field which has lain dormant until
 * now.
 */
static
void    SelectSortByRange(TSelect* Table, int Size)
    {
    TSelect*    Select;
    int         iSelect, iToken, Range, Start;
    
printf("SelectSortByRange(%d)\n", Size);
    for(iSelect=0; iSelect < Size; ++iSelect)
        {
        Select  = Table + iSelect;
        /* sort tokens in ascending order */
        qsort(Select->Tokens.v, Select->Tokens.Size, sizeof(int), IntCmp);
{
int i;
printf("after qsort in ascending order, token values are:\n");
for(i = 0; i < Select->Tokens.Size; ++i)
    printf("%d, ", Select->Tokens.v[i]);
printf("\n");
}
        Range   = 1;
        Start   = 0;
        /* start at iToken==1 because we're always looking back*/
        for(iToken=1; iToken < Select->Tokens.Size; ++iToken)
            {
printf("[%d/%d]=%d Range=%d [%d-%d]\n", iToken, Select->Tokens.Size,
       IntGet(&Select->Tokens,iToken), Range, Start, Start+Range);
            /* if this token value goes with current "range" */
            if(IntGet(&Select->Tokens,iToken-1) + 1 == IntGet(&Select->Tokens,iToken))
                {
                /* then bump size of this range, and track max range seen */
                if(++Range > Select->MaxRange)
                    Select->MaxRange    = Range;
                }
            else
                {
                if(Range >= MINRANGE)
                    SelectAddRange(Select, Select->Tokens.v[Start], Select->Tokens.v[(Start+Range)-1]);
                Start   = iToken;
                Range   = 1;
                }
            }
        if(Range >= MINRANGE)
            SelectAddRange(Select, Select->Tokens.v[Start], Select->Tokens.v[(Start+Range)-1]);

printf("maximum size range was %d\n", Select->MaxRange);
        }
    qsort(Table, Size, sizeof(*Table), SelectCmpRange);
    }

/* SelectSortByCount() - sort the selection table by count.
 *
 * We sort by which rule has the biggest count of associated
 * tokens.  Almost. Note that we have to ignore ranges of
 * tokens that were already used in the code generation.
 * Somebody else kindly set those token values to -1 to
 * make our life easier.
 */
static
void    SelectSortByCount(TSelect* Table, int Size)
    {
    TSelect*    Select;
    TSelRange*  Range;
    int         iSelect, iTokenVal, iToken, Count;
    
    /* first, we must remove the token ranges already used */
    for(iSelect=0; iSelect < Size; ++iSelect)
        {
        Select  = Table + iSelect;
        Range   = Select->Ranges;
        while(Range)
            {
            for(iTokenVal=Range->Start; iTokenVal <= Range->End; ++iTokenVal)
                {
//                assert(iToken < Select->Tokens.Size);
                for(iToken=0; iToken < Select->Tokens.Size; ++iToken)
                    if(Select->Tokens.v[iToken] == iTokenVal)
                        Select->Tokens.v[iToken]    = -1;
                }
            Range   = Range->Next;
            }
        }

    for(iSelect=0; iSelect < Size; ++iSelect)
        {
        Select  = Table + iSelect;
        Count   = 0;
        for(iToken=0; iToken < Select->Tokens.Size; ++iToken)
            {
printf("[%d]=%d  ", iToken, Select->Tokens.v[iToken]);
            if(Select->Tokens.v[iToken] != -1)
                ++Count;
            }
        Select->Count   = Count;
printf("\n***setting count to %d\n", Select->Count);
        }
    qsort(Table, Size, sizeof(*Table), SelectCmpCount);
Dump("SelectSortByCount() returns\n");
    }


/* TPatch - linked list for remembering back patches
 */
typedef struct  TPatch
    {
    size_t      Offset;
    int         RuleId;
    struct TPatch*  Next;
    }           TPatch;

static TPatch*  BackPatchList;

/* MarkPatch() - remember a location for future patching.
 */
static
int     MarkPatch(int IP, int RuleId)
    {
    TPatch* Patch   = NEW(TPatch);
    assert(Patch != NULL);
    Patch->Offset   = IP;
    Patch->RuleId   = RuleId;
    Patch->Next     = BackPatchList;
    BackPatchList   = Patch;

    return IP + 2;
    }

/* BackPatch() - backpatch some parts of the opcodes
 *
 * We have a linked list of offsets into Opcodes that need
 * to be patched. Each patch has a rule # associated with it.
 * As each rule is emitted, its actual offset into Opcodes
 * becomes known. Given a rule # and an offset, we locate
 * any previous calls to MarkPatch(), patch the Value into
 * Opcodes, then release the patch.
 */
static
void    BackPatch(TParseTables* Tables, int RuleId, int Value)
    {
    TPatch**    Rover;
    TPatch*     Temp;
    int         Offset;

    Rover   = &BackPatchList;
    while(*Rover)
        {
        Temp                = *Rover;
        if(Temp->RuleId == RuleId)
            {
            Offset              = Temp->Offset;
            Store16(Tables, Offset, Value);
            *Rover              = Temp->Next;
            free(Temp);
            }
        else
            Rover   = &Temp->Next;
        }
    }

/* DefineTokens() - assign integer IDs to tokens as needed.
 *
 * The input grammar specification may include token names without
 * assigning them any integer value. Here is where we assign integers
 * to any such token names.
 */
static
void        DefineTokens(TParseTables* Tables)
    {
    int         NTerminals, iTerminal, InfiniteLoop;
    TSymbol*    Terminal;
    int         NextId;

    Tables->MaxTokenVal = 0;
    Tables->MinTokenVal = 0;

    /* for each terminal */
    NextId      = 0;
    NTerminals  = SymbolListCount(Globals.Terminals);
    for(iTerminal = 0; iTerminal < NTerminals; ++iTerminal)
        {
        Terminal    = SymbolListGet(Globals.Terminals, iTerminal);
        assert(Terminal != NULL);

        /* want NoAction to be set to 0 anyway */
        if(Terminal->Value == 0 && Terminal != NoActionSymbol)
            {
            for(InfiniteLoop=NextId+1024*8; SymbolFromValue(NextId) != NULL; ++NextId)
                assert(NextId < InfiniteLoop);    /* sanity check */

            SymbolSetValue(Terminal, NextId++);
            }
        if(Terminal == EolSymbol)
            Tables->TokenEol = EolSymbol->Value;
        else if(Terminal == MatchAnySymbol)
            Tables->TokenEol = MatchAnySymbol->Value;

        if(Terminal->Value < Tables->MinTokenVal)
            Tables->MinTokenVal = Terminal->Value;
        if(Terminal->Value > Tables->MaxTokenVal)
            Tables->MaxTokenVal = Terminal->Value;
        }
printf("maxtok=%d mintok=%d nTerminals=%d\n",
       Tables->MaxTokenVal, Tables->MinTokenVal, NTerminals);
    assert((Tables->MaxTokenVal - Tables->MinTokenVal)+1 >= (NTerminals-1));
    }

/* GetLLNonTerms() - get the list of LL grammar nonterminals we'll be using.
 *
 * For the LL(1) parser tables, a variety of nonterminals are irrelevant,
 * so we just make a list of the ones that matter here.
 */
SYMBOLS     GetLLNonTerms(void)
    {
    int         NNonTerms, iNonTerm;
    TSymbol*    NonTerm;
    SYMBOLS     Result;

    Result      = NULL;
    NNonTerms   = SymbolListCount(Globals.NonTerms);
    for(iNonTerm = 0; iNonTerm < NNonTerms; ++iNonTerm)
        {
        NonTerm     = SymbolListGet(Globals.NonTerms, iNonTerm);
        
        /* if it belongs to operator precedence grammar */
        if(NonTerm->OperatorTrigger)
            continue;   /* then skip it */
        /* ignore dummy nonterminals we created for actions */
        else if(SymbolIsAction(NonTerm))
            continue;
        /* no need to store data for implicit start symbol */
        else if(NonTerm == SymbolStart())
            continue;
        /* otherwise, it's a nonterminal we care about! */
        Result  = SymbolListAdd(Result, NonTerm);
        }

    return Result;
    }

#if 0
static
void        StartRow(TParseTables* Tables)
    {
    /* record offset of where next variable-length row starts */
    IntAdd(&Tables->LLRowStarts, Tables->LLRows.Size);
    }

static
void        EndRow(TParseTables* Tables)
    {
    /* add sentinel marker to token ID array */
    IntAdd(&Tables->LLColumns, EolSymbol->Value);
    /* add dummy spacer to LLRows, to keep it same size as LLColumns */
    IntAdd(&Tables->LLRows, 0);
    }
#endif

/* RulesEqual() - are two rules equivalent?
 *
 * This is a modest optimization to avoid storing space for
 * two different rules that are equivalent. It is usually a
 * productive optimization, simply because there will be
 * multiple rules consisting of a null transition and no action.
 */
static
int     RulesEqual(TRule* A, TRule* B)
    {
    return SymbolListEqual(A->Symbols, B->Symbols);
    }

static
int    AddRule(TParseTables* Tables, TRule* Rule, TSymbol* NonTerm)
    {
    int     iRule;

    /* scan list of existing unique rules   */
    for(iRule = 0; iRule < Tables->NRules; ++iRule)
        if(RulesEqual(Tables->Rules[iRule], Rule))
            {
            SymbolListAddUnique(Tables->RuleSymbols[iRule], NonTerm);
            break;
            }
    /* if this is a new unique rule     */
    if(iRule >= Tables->NRules)
        {
        int     NewRuleCount = Tables->NRules+1;
        
        Tables->Rules   = realloc(Tables->Rules, NewRuleCount*sizeof(Tables->Rules[0]));
        assert(Tables->Rules != NULL);
        Tables->RuleSymbols = realloc(Tables->RuleSymbols, NewRuleCount*sizeof Tables->RuleSymbols[0]);
        assert(Tables->RuleSymbols != NULL);
        Tables->Rules[Tables->NRules]   = Rule;
        Tables->RuleSymbols[Tables->NRules] = SymbolListAdd(NULL, NonTerm);
        Rule->RuleId                    = Tables->NRules;
        Tables->NRules                  = NewRuleCount;
        }
    else
        Rule->RuleId    = Tables->Rules[iRule]->RuleId;

    return Rule->RuleId;
    }

/* GenerateSelect() - generate the body of the SELECT opcode.
 *
 * The SELECT "body" consists of data for multiple algorithms
 * that associate token values with rules. The "outer" structure
 * for each algorithm looks like this:
 *
 * +-------------+
 * | entry count |  1 byte
 * +-------------+
 * | ... data    |  variable length
 * +-------------+
 *
 * This data structure repeats (if necessary) and is terminated
 * with an "entry count" of 0. Note that a particular algorithm
 * may not be used for a particular SELECT body, in which case
 * its very first "entry count" will be zero. IOW, it only costs
 * a single null byte to skip over any algorithm deemed unproductive
 * for a given SELECT body. The algorithms, in the order they
 * will be expected by the interpreter, are:
 *
 * 1. Many tokens -> 1 rule
 * +-------------+
 * | token count |  1 byte
 * +-------------+
 * | rule        |  2 bytes (rule offset)
 * +-------------+
 * | N tokens    |  variable length
 * +-------------+
 *
 */
static
size_t GenerateSelect(TParseTables* Tables, size_t IP, TSymbol* Symbol)
    {
    TSelect*    SelSet;
    TSelect*    Select;
    int         SelSetSize;
    TSymbol*    Input;
    int         iSelect, iRule, iToken, NTokens, NRanges;
    TRule*      Rule;
    SYMBOLS     NextSyms;
    
    NextSyms    = Symbol->InputTokens;
    /* if tail recursion, we only care about FIRST() set */
    if(Symbol->Name.Text[0] == '`')
        NextSyms    = Symbol->First;
    /* How many tokens do we have legal transitions for? */
    NTokens     = SymbolListCount(NextSyms);
    assert(NTokens < 255);

    /* remember our opcode offset; later instructions will need it */
    Symbol->SelectOffset    = IP - Tables->SelSectOfs;
printf("create selection set table\n");

    /* create selection set table */
    SelSetSize  = 0;
    SelSet      = SelectNew(NTokens);
    /* for each token this nonterminal can transition on */
    for(iToken = 0; iToken < NTokens; ++iToken)
        {
        Input   = SymbolListGet(NextSyms, iToken);
        assert(Input != NULL);

        /* find rule we transition to on this token */
        iRule   = Symbol->LLTable[iToken];
        assert(iRule < RuleCount(Symbol));
        assert(iRule >= 0);
        Rule    = Symbol->Rules[iRule];
        iRule = AddRule(Tables, Rule, Symbol);
        assert(Input->Value < 255);
printf("%s on %s (%d) -> %d\n", SymbolStr(Symbol), SymbolStr(Input), Input->Value, iRule);
        SelSetSize  = SelectAdd(SelSet, SelSetSize, iRule, Input);
        }
printf("NTokens=%d, SelSetSize(# of different rules)=%d\n", NTokens, SelSetSize);
printf("sort by biggest ranges\n");
    /* sort by biggest ranges */
    SelectSortByRange(SelSet, SelSetSize);

    /* Do the selections with sufficiently big ranges */
    for(iSelect = 0; iSelect < SelSetSize; ++iSelect)
        {
        TSelRange* RangeRover;

        Select  = SelSet + iSelect;
        NRanges = Select->NRanges;
printf("NRanges=%d\n", NRanges);
            
        if(NRanges > 0)
            {
            IP  = Store8(Tables, IP, NRanges);
            IP  = MarkPatch(IP, Select->Rule);
            for(RangeRover=Select->Ranges; RangeRover; RangeRover=RangeRover->Next)
                {
                IP  = Store8(Tables, IP, RangeRover->Start);
                IP  = Store8(Tables, IP, RangeRover->End);
                }
            }
        else
            break;
        }
    IP  = Store8(Tables, IP, 0);    /* sentinel byte */

printf("There were %d selection set ranges for '%s'\n", iSelect, SymbolStr(Symbol));
    /* sort by biggest token counts per rule */
    SelectSortByCount(SelSet, SelSetSize);
    /* Now emit in order of largest token counts */
    for(iSelect = 0; iSelect < SelSetSize; ++iSelect)
        {
        int         TokenVal;
        int         Count;

        Select  = SelSet + iSelect;
        Count   = Select->Count;

printf("Count=%d\n", Count);
            
        if(Count > 0)
            {
            IP  = Store8(Tables, IP, Count);
            IP  = MarkPatch(IP, Select->Rule);
            for(iToken=0; iToken < Select->Tokens.Size; ++iToken)
                {
                TokenVal    = Select->Tokens.v[iToken];
                if(TokenVal >= 0)
                    IP  = Store8(Tables, IP, TokenVal);
                }
            }
        }
    IP  = Store8(Tables, IP, 0);    /* sentinel byte */

    SelectDestroy(SelSet, SelSetSize);
Dump("GenerateSelect() returns IP=%d\n", IP);
    return IP;
    }

TParseTables* GenerateParseTables(void)
    {
    size_t          IP, IPStart;
    int             NNonTerms, NSymbols;
    int             iRule, iTerminal;
    SymIt           NonTerm;
    TSymbol*        Symbol;
    TParseTables*   Tables;
    SYMBOLS         LLNonTerms;
    int             Reduced;
    int             TerminalsEmitted;

printf("GenerateParseTables() begins\n");
    Tables      = NEW(TParseTables);
    assert(Tables != NULL);

    Tables->LLNonTerms = LLNonTerms = GetLLNonTerms();   /* get list of only nonterminals we care about */
    
    /* assign integers to any undefined tokens */
    DefineTokens(Tables);
    
    IP              = BLC_HDR_SIZE;       /* skip over initial header containing 5 two-byte table sizes */
    NNonTerms       = SymbolListCount(LLNonTerms);

    /* generate terminal symbol table */
    IPStart             = IP;
    TerminalsEmitted    = 0;
    for(iTerminal=Tables->MinTokenVal; iTerminal <= Tables->MaxTokenVal; ++iTerminal)
        {
        Symbol  = SymbolFromValue(iTerminal);
        if(Symbol)
            {
            /* store token ID, followed by its null-terminated string */
            IP  = Store8(Tables, IP, iTerminal);
            IP  = StoreStr(Tables, IP, SymbolStr(Symbol));
            ++TerminalsEmitted;
            }
        }

    IP  = Store8(Tables, IP, 0);    /* sentinel byte */
    Store16(Tables, BLC_HDR_TERMSYMTAB_SIZE, IP - IPStart);

    /* generate nonterminal symbol table */
    IPStart         = IP;
    NonTerm = SymItNew(LLNonTerms);
    while(SymbolIterate(&NonTerm))
        {
        IP  = StoreStr(Tables, IP, SymbolStr(NonTerm.Symbol));
        }
    IP  = Store8(Tables, IP, 0);    /* sentinel byte */
    Store16(Tables, BLC_HDR_NONTERMSYMTAB_SIZE, IP - IPStart);

    /* for each production of <start>, add an entry point */
    IPStart = IP;
    Symbol  = SymbolStart();
    assert(Symbol != NULL);
    for(iRule = 0; iRule < RuleCount(Symbol); ++iRule)
        {
        /* actual value will have to be backpatched */
        MarkPatch(IP, AddRule(Tables, Symbol->Rules[iRule], Symbol));
        IP  = Store16(Tables, IP, 0);
        }
    Store16(Tables, BLC_HDR_ENTRYTABLE_SIZE, IP - IPStart);

    /* for each nonterminal, generate its SELECT body*/
    IPStart             = IP;
    Tables->SelSectOfs  = IPStart;
    NonTerm = SymItNew(LLNonTerms);
    while(SymbolIterate(&NonTerm))
        IP = GenerateSelect(Tables, IP, NonTerm.Symbol);
    Store16(Tables, BLC_HDR_SELECTTABLE_SIZE, IP - IPStart);

    Dump("Generate opcodes for each unique rule\n");

    /* generate opcodes for each unique rule */
    IPStart             = IP;
    Tables->RuleSectOfs = IPStart;
    for(iRule = 0; iRule < Tables->NRules; ++iRule)
        {
        int     iProdItem;
        TRule*  Rule        = Tables->Rules[iRule];

        IntAdd(&Tables->RuleOffsets, IP);
        Reduced     = FALSE; /* have not performed a reduction for this rule yet */
        NSymbols    = SymbolListCount(Rule->Symbols);
fprintf(stdout, "-> ");
SymbolListDump(stdout, Rule->Symbols, " ");
fprintf(stdout, "\n");

        for(iProdItem = 0; iProdItem < NSymbols; ++iProdItem)
            {
            TSymbol*    Symbol = SymbolListGet(Rule->Symbols, iProdItem);

            if(SymbolGetBit(Symbol, SYM_TERMINAL))
                {
                IP      = Store8(Tables, IP, BLCOP_MATCH);
                IP      = Store8(Tables, IP, Symbol->Value);

                if(Symbol == SymbolFind(EOFToken))
                    {
                    IP      = Store8(Tables, IP, BLCOP_HALT);
                    Reduced = TRUE;
                    }
                }
            else if(SymbolIsAction(Symbol))
                {
                int  FinalAction, ArgCount;

                assert(Rule->RuleId < 255);
                assert(iProdItem < 255);

                FinalAction = FALSE;
                if(iProdItem == NSymbols-1)
                    FinalAction = TRUE;
                else if(Rule->TailRecursive && iProdItem == NSymbols-2)
                    FinalAction = TRUE;

                ArgCount = iProdItem;
                if(Rule->TailRecursive == 2)
                    ++ArgCount;
fprintf(stdout, "iProdItem=%d, NSymbols=%d,TailRecursive=%d,FinalAction=%d\n", iProdItem, NSymbols, Rule->TailRecursive, FinalAction);
                IP  = Store8(Tables, IP, FinalAction?BLCOP_ACTRED:BLCOP_ACTION8);
                IP  = Store8(Tables, IP, Symbol->Action->Number);
                IP  = Store8(Tables, IP, ArgCount);
                Symbol->Action->ArgCount = ArgCount;
                if(FinalAction)
                    Reduced = TRUE;
                }
            else if(SymbolListContains(LLNonTerms, Symbol))    /* non-terminal */
                {
                int iSymbol = SymbolListContains(LLNonTerms, Symbol);

                if(RuleCount(Symbol) > 1)
                    {
                    if(Symbol->Name.Text[0] == '`') /* if a tail recursive rule... */
                        {
                        IP      = Store8(Tables, IP, BLCOP_TAILSELECT);
                        IP      = Store8(Tables, IP, iProdItem);
                        Reduced = TRUE;    /* TAILSELECT opcode must do the reducing to shuffle stack correctly */
                        }
                    else
                        IP      = Store8(Tables, IP, BLCOP_LLSELECT);
                    IP      = Store16(Tables, IP, Symbol->SelectOffset);
                    }
                /* else, only 1 rule to choose from, so just transfer control to that rule! */
                else
                    {
                    int     iRule = AddRule(Tables, Symbol->Rules[0], Symbol);
                    
                    IP      = Store8(Tables, IP, BLCOP_CALL);
                    IP      = MarkPatch(IP, iRule);
                    }

                assert(iSymbol>0);  /* ???TODO why is this???*/
                }
            else // else, it's an operator trigger
                {
                assert(Symbol->LR0 != NULL);
                fprintf(stderr, "watch out: we don't handle LR(0) yet!\n");
                }
            }
        if(!Reduced)
            {
            IP  = Store8(Tables, IP, BLCOP_REDUCE);
            IP  = Store8(Tables, IP, NSymbols);
            }
        }
    Store16(Tables, BLC_HDR_RULEOPCODE_SIZE, IP - IPStart);


    DumpVerbose("Backpatch opcode addresses.\n");
    for(iRule = 0; iRule < Tables->NRules; ++iRule)
        BackPatch(Tables, iRule, Tables->RuleOffsets.v[iRule] - IPStart);
    DumpVerbose("Backpatching complete.\n");


    assert(IP < (1024*64));

//    Opcodes             = realloc(Opcodes, IP * sizeof(Opcodes[0]));
    Tables->NOpcodes    = IP;
    Dump("GenerateParseTables() returns after %d opcodes\n", Tables->NOpcodes);
    return Tables;
    }

void    FreeParseTables(TParseTables* Tables)
    {
    if(Tables)
        {
        if(Tables->LLNonTerms)
            SymbolListDestroy(Tables->LLNonTerms);
        if(Tables->Rules)
            free(Tables->Rules);
        if(Tables->RuleOffsets.v)
            free(Tables->RuleOffsets.v);
        free(Tables->Opcodes);
        free(Tables);
        }
    }

